#ifndef SAFE_TIMER_H
#define SAFE_TIMER_H

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <utility>

class SafeTimer {
 public:
  explicit SafeTimer(const std::string& name = "SafeTimer") noexcept;
  virtual ~SafeTimer() noexcept;

  // Copy is not allowed
  SafeTimer(const SafeTimer&) = delete;
  SafeTimer& operator=(const SafeTimer&) = delete;

  std::string GetName() const noexcept;
  bool IsLoop() const noexcept;

  template <typename Callable, typename... Arguments>
  bool SingleShot(uint64_t interval_in_millis, Callable&& func, Arguments&&... args);

  template <typename Callable, typename... Arguments>
  bool Repeat(uint64_t interval_in_millis, Callable&& func, Arguments&&... args);

  template <typename Callable, typename... Arguments>
  bool Repeat(uint64_t interval_in_millis, bool call_func_immediately, Callable&& func, Arguments&&... args);

  void Cancel() noexcept;
  bool Pause() noexcept;
  void Resume() noexcept;
  bool IsTimerIdle() const noexcept;

 private:
  bool Start(uint64_t interval_in_millis, std::function<void()> callback, bool loop, bool callback_immediately = false);
  void TryExpire() noexcept;
  void DestroyThread() noexcept;

 private:
  std::string name_;
  bool is_loop_;
  std::atomic_bool is_expired_;
  std::atomic_bool try_to_expire_;
  std::unique_ptr<std::thread> thread_;
  std::mutex mutex_;
  std::condition_variable condition_;
  std::chrono::time_point<std::chrono::steady_clock> start_time_;
  std::chrono::time_point<std::chrono::steady_clock> end_time_;
  uint64_t task_remain_time_ms_;
  std::function<void()> callback_;
};

template <typename Callable, typename... Arguments>
bool SafeTimer::SingleShot(uint64_t interval_in_millis, Callable&& func, Arguments&&... args) {
  auto action = std::bind(std::forward<Callable>(func), std::forward<Arguments>(args)...);
  return Start(interval_in_millis, action, false);
}

template <typename Callable, typename... Arguments>
bool SafeTimer::Repeat(uint64_t interval_in_millis, Callable&& func, Arguments&&... args) {
  auto action = std::bind(std::forward<Callable>(func), std::forward<Arguments>(args)...);
  return Start(interval_in_millis, action, true);
}

template <typename Callable, typename... Arguments>
bool SafeTimer::Repeat(uint64_t interval_in_millis, bool call_func_immediately, Callable&& func, Arguments&&... args) {
  auto action = std::bind(std::forward<Callable>(func), std::forward<Arguments>(args)...);
  return Start(interval_in_millis, action, true, call_func_immediately);
}

#endif  // SAFE_TIMER_H
